package org.example.mini_spring;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;

//创建对象，保存对象
public class ApplicationContext {


    public ApplicationContext(String scanPackage) throws Exception {
        initContext(scanPackage);
    }

    private void initContext(String scanPackage) throws Exception {
        //生成beanDefinition
        scanPackage(scanPackage).stream().filter(this::canCreate).forEach(this::wrapper);

        initBeanPostProcessor();

        beanDefinitionMap.values().forEach(this::createBean);
    }

    private void initBeanPostProcessor() {

        beanDefinitionMap.values().stream()
                .filter(bd -> BeanPostProcessor.class.isAssignableFrom(bd.getBeanType()))
                .map(this::createBean)
                .map(BeanPostProcessor.class::cast)
                .forEach(postProcessors::add);


    }

    private Object createBean(BeanDefinition beanDefinition) {
        String name = beanDefinition.getName();

        if (ioc.containsKey(name)) {
            return ioc.get(name);
        }

        if (loadingIoc.containsKey(name)) {
            return loadingIoc.get(name);
        }

        return doCreateBean(beanDefinition);
    }

    private Object doCreateBean(BeanDefinition beanDefinition) {
        Constructor<?> constructor = beanDefinition.getConstructor();

        Object bean = null;

        try {
            bean = constructor.newInstance();
            loadingIoc.put(beanDefinition.getName(),bean);
            autowiredBean(bean, beanDefinition);
            bean = initializeBean(bean, beanDefinition);
            loadingIoc.remove(beanDefinition.getName());
            ioc.put(beanDefinition.getName(),bean);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return bean;
    }

    private Object initializeBean(Object bean, BeanDefinition beanDefinition) throws InvocationTargetException, IllegalAccessException {
        for (BeanPostProcessor postProcessor : postProcessors) {
            bean = postProcessor.beforeInitializeBean(bean,beanDefinition.getName());
        }

        Method postConstructMethod = beanDefinition.getPostConstructMethod();

        if (postConstructMethod != null) {
            postConstructMethod.invoke(bean);
        }

        for (BeanPostProcessor postProcessor : postProcessors) {
            bean = postProcessor.afterInitializeBean(bean,beanDefinition.getName());
        }

        return bean;
    }

    private void autowiredBean(Object bean, BeanDefinition beanDefinition) throws IllegalAccessException {
        for (Field autowireField : beanDefinition.getAutowireFields()) {
            autowireField.setAccessible(true);
            autowireField.set(bean,getBean(autowireField.getType()));
        }
    }

    //存储bean类信息
    private final Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();

    private final Map<String,Object> ioc = new HashMap<>();

    private final Map<String,Object> loadingIoc = new HashMap<>();

    private final List<BeanPostProcessor> postProcessors = new ArrayList<>();


    private boolean canCreate(Class<?> beanType){
        return beanType.isAnnotationPresent(Component.class);
    }

    private BeanDefinition wrapper(Class<?> beanType){

        BeanDefinition beanDefinition = new BeanDefinition(beanType);
        if (beanDefinitionMap.containsKey(beanDefinition.getName())) {
            throw new RuntimeException("bean名称重复");
        }
        beanDefinitionMap.put(beanDefinition.getName(),beanDefinition);
        return beanDefinition;
    }

    private List<Class<?>> scanPackage(String packageName) throws Exception {
        List<Class<?>> classList = new ArrayList<>();

        URL resource = this.getClass().getClassLoader().getResource(packageName.replace(".", File.separator));
        Path path = Path.of(resource.toURI());
        Files.walkFileTree(path,new SimpleFileVisitor<>(){

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException{

                Path absolutePath = file.toAbsolutePath();
                if (absolutePath.toString().endsWith(".class")) {
                    String replace = absolutePath.toString().replace(File.separator, ".");
                    int replaceIndex = replace.indexOf(packageName);
                    String className = replace.substring(replaceIndex, replace.length() - ".class".length());
                    try {
                        classList.add(Class.forName(className));
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                }
                return FileVisitResult.CONTINUE;
            }
        });

        return classList;
    }

//    获取bean对象
    public Object getBean(String beanName){
        return null;
    }

    public <T> T getBean(Class<T> beanType){
        return null;
    }

    public <T> List<T> getBeans(Class<T> beanType){
        return null;
    }

}
